<template>
	<view>
		<view class="content" @touchstart="hideEmoji">
			<scroll-view class="msg-list" scroll-y="true" upper-threshold="5" :scroll-with-animation="scrollAnimation"
				:scroll-top="scrollTop" :scroll-into-view="scrollToView" @scrolltoupper="getMsgList(0)">
				<view class="row" v-for="(row,index) in msgList" :key="index" :id="'msg'+index">
					<!-- 自己发出的消息 -->
					<view class="my" v-if="row.UserId==myuid">
						<view class="left">
							<view v-if="row.MsgType==0" class="bubble">
								<rich-text :nodes="row.Contents"></rich-text>
							</view>
							<!-- <view v-if="row.type==3" class="bubble voice" @tap="playVoice(row)"
								:class="playMsgid == row.id?'play':''">
								<view class="length">{{row.msg.length}}</view>
								<view class="icon my-voice"></view>
							</view> -->
							<view v-if="row.MsgType==1" class="bubble img" @tap="showPic(row)">
								<image :src="getUrl(row.Contents)" :style="{'width':160+'rpx','height': 160+'rpx'}"
									class="imgmax">
								</image>
							</view>
						</view>
						<view class="right">
							<image :src="getUrl(row.UserId==msgModel.UserId?msgModel.Avatar:msgModel.TowAvatar)">
							</image>
						</view>
					</view>
					<!-- 别人发出的消息 -->
					<view class="other" v-if="row.ToUserId==myuid">
						<view class="left">
							<image :src="getUrl(row.UserId==msgModel.UserId?msgModel.Avatar:msgModel.TowAvatar)"
								widthFix>
							</image>
						</view>
						<view class="right">
							<view class="username">
								<view class="name">
									{{row.UserId==msgModel.UserId?msgModel.UserName:msgModel.TowUserName}}
								</view>
								<view class="time">{{$u.timeFormat(row.CrateTime,'hh:MM')}}</view>
							</view>
							<view v-if="row.MsgType==0" class="bubble">
								<rich-text :nodes="row.Contents"></rich-text>
							</view>
							<!-- <view v-if="row.type=='voice'" class="bubble voice" @tap="playVoice(row)"
								:class="playMsgid == row.id?'play':''">
								<view class="icon other-voice"></view>
								<view class="length">{{row.msg.length}}</view>
							</view> -->
							<view v-if="row.MsgType==1" class="bubble img" @tap="showPic(row)">
								<image :src="getUrl(row.Contents)" :style="{'width':160+'rpx','height': 160+'rpx'}"
									class="imgmax">
								</image>
							</view>
						</view>
					</view>
				</view>
			</scroll-view>
		</view>
		<!-- 表情栏 -->
		<view class="emoji-box" v-if="false" :class="showEmji" @touchmove.stop.prevent="discard">
			<swiper class="swiper" indicator-dots="true" duration="150">
				<swiper-item v-for="(page,pid) in emojiList" :key="pid">
					<view v-for="(em,eid) in page" :key="eid" @tap="addEmoji(em)">
						<image mode="widthFix" :src="'../../static/img/emoji/'+em.url"></image>
					</view>
				</swiper-item>
			</swiper>
		</view>
		<!-- 底部输入栏 -->
		<view class="input-box" :class="opentext==true?'shuoopenbtm':showEmji" @touchmove.stop.prevent="discard">
			<!-- H5下不能录音，输入栏布局改动一下 -->
			<!-- #ifndef H5 -->
			<!-- <view class="voice">
				<view class="icon" :class="isVoice?'jianpan':'yuyin'" @tap="switchVoice"></view>
			</view> -->
			<!-- #endif -->
			<!-- #ifdef H5 -->
			<!-- <view class="more" @tap="chooseImage">
				<view class="icon tupian"></view>
			</view> -->
			<!-- #endif -->
			<view class="textbox">
				<view class="voice-mode" :class="[isVoice?'':'hidden',recording?'recording':'']"
					@touchstart="voiceBegin" @touchmove.stop.prevent="voiceIng" @touchend="voiceEnd"
					@touchcancel="voiceCancel">{{voiceTis}}</view>
				<view class="text-mode" :class="isVoice?'hidden':''">
					<view class="box">
						<!-- <input placeholder="请输入..." v-model="textMsg" style="width: 100%;font-size: 16px;"/> -->
						<textarea style="width: 100%;font-size: 16px;" :auto-height="true" placeholder="请输入..."
							v-model="textMsg" id="textMsg" @focus="opentext=true" @blur="opentext=false" />
					</view>
					<!-- <view class="em" @tap="chooseEmoji">
						<view class="icon biaoqing"></view>
					</view> -->
				</view>
			</view>
			<!-- #ifndef H5 -->
			<!-- <view class="more" @tap="chooseImage">
				<view class="icon tupian"></view>
			</view> -->
			<!-- #endif -->
			<label for="textMsg">
				<view class="send" :class="isVoice?'hidden':''" @tap="sendText">
					<view class="btn">
						发送
					</view>
				</view>
			</label>
		</view>
		<!-- 录音效果(上滑取消) -->
		<view class="record" :class="recording?'':'hidden'">
			<view class="ing" :class="willStop?'hidden':''">
				<view class="icon luyin2"></view>
			</view>
			<view class="cancel" :class="willStop?'':'hidden'">
				<view class="icon chehui"></view>
			</view>
			<view class="tis" :class="willStop?'change':''">{{recordTis}}</view>
		</view>
	</view>
</template>

<script>
	import {
		getMsg,
		loadMsgLogList
	} from '@/common/notic.js'

	export default {
		data() {
			return {
				msgId: '',
				msgModel: {},
				//文字消息
				textMsg: '',
				msgTotal: 0,
				msgQuery: {
					page: 0,
					limit: 10,
					queryStates:1,//0-未读 1-已读
					queryId: ''
				},
				msgList: [], //消息列表

				msgImgList: [],
				myuid: 0, //我的用户Id
				touid: 0, //对方用户Id
				opentext: false, //文本域上推

				url: '', //地址
				socketTask: null,
				is_open_socket: false, //避免重复连接
				isopensocket: false, //是否连接(手动)
				//心跳检测
				timeout: 60000, //多少毫秒执行检测 1秒=1000毫秒
				heartbeatInterval: null, //检测服务器端是否还活着
				reconnectTimeOut: null, //重连之后多久再次重连

				scrollAnimation: false,
				scrollTop: 0,
				scrollToView: '',
				//录音相关参数
				// #ifndef H5
				//H5不能录音
				RECORDER: uni.getRecorderManager(),
				// #endif
				isVoice: false,
				voiceTis: '按住 说话',
				recordTis: "手指上滑 取消发送",
				recording: false,
				willStop: false,
				initPoint: {
					identifier: 0,
					Y: 0
				},
				recordTimer: null,
				recordLength: 0,
				//播放语音相关参数
				AUDIO: uni.createInnerAudioContext(),
				playMsgid: null,
				VoiceTimer: null,
				//表情定义
				showEmji: '',
				emojiList: [],
				//表情图片图床名称
				onlineEmoji: {}
			};
		},
		onLoad(option) {
			console.log('我进来了')
			if (option.id) this.msgId = parseInt(option.id)
			this.msgQuery.queryId = this.msgId;
			this.getMsgList(1)
			//语音自然播放结束
			this.AUDIO.onEnded((res) => {
				this.playMsgid = null;
			});
			// #ifndef H5
			//录音开始事件
			this.RECORDER.onStart((e) => {
				this.recordBegin(e);
			})
			//录音结束事件
			this.RECORDER.onStop((e) => {
				this.recordEnd(e);
			})
			// #endif
		},
		onShow() {
			this.loadInfo()
		},
		onUnload() {
			this.closeSocket()
		},
		methods: {
			async loadInfo() {
				var rs = await getMsg({
					queryId: this.msgId
				})
				if (rs.code == 200) {
					this.myuid = rs.data.myUserId
					this.msgModel = rs.data.data
					let title = (this.myuid == this.msgModel.UserId ?
						this.msgModel.TowUserName : this.msgModel.UserName);
					uni.setNavigationBarTitle({
						title: title
					})
					this.touid = (this.myuid == this.msgModel.UserId ? this.msgModel.TowUserId : this.msgModel.UserId)
					let http = 'wx.dangwanjia.com' // 'localhost:55162'//

					// #ifdef APP-PLUS
					this.url = `ws://${http}/ws?usermsgCode=${this.myuid}_${this.msgId}`
					// #endif

					// #ifdef MP-WEIXIN
					this.url = `ws://${http}/ws?usermsgCode=${this.myuid}_${this.msgId}`
					// #endif
					this.connectSocketInit()
				}
			},
			async getMsgList(load = 0) {
				// 消息列表
				if (load > 0) {
					this.msgQuery.page = 0
					this.msgTotal = 0
					this.msgList = []
				}
				if (load > 0 || this.msgTotal > (this.msgQuery.page * this.msgQuery.limit)) {
					this.msgQuery.page += 1
					var rs = await loadMsgLogList(this.msgQuery)
					if (rs.code == 200) {
						console.log(rs)
						this.msgTotal = rs.data.total
						//this.list = this.list.concat(rs.data.items)
						let list = rs.data.items
						// // 获取消息中的图片,并处理显示尺寸
						// for (let i = 0; i < list.length; i++) {
						// 	if (list[i].MsgType == 1) {
						// 		list[i] = this.setPicSize(list[i]);
						// 		console.log("list[i]: " + JSON.stringify(list[i]));
						// 		this.msgImgList.push(list[i].msg.url);
						// 	}
						// }
						for (var i = 0; i < list.length; i++) {

						}

						this.msgList = list.concat(this.msgList)
						//console.log(this.msgList)
					}
				}
				// 滚动到底部
				this.$nextTick(function() {
					this.scrollTop = 9999;
					this.scrollAnimation = true;
					if (load > 0) {
						var len = this.msgList.length
						if (len > 0) len = len - 1
						this.scrollToView = 'msg' + len
					}
				});
			},
			//开始连接
			connectSocketInit() {
				this.socketTask = uni.connectSocket({
					url: this.url,
					success: () => {
						console.log("正准备建立websocket中...");
						// 返回实例
						return this.socketTask
					},
				});
				this.socketTask.onOpen((res) => {
					console.log("WebSocket连接正常！");
					clearTimeout(this.reconnectTimeOut)
					clearTimeout(this.heartbeatInterval)
					this.is_open_socket = true;
					this.isopensocket = true;
					this.start();
					// 注：只有连接正常打开中 ，才能正常收到消息
					this.socketTask.onMessage((res) => {
						console.log(res.data)
						if (res.data != 'ping'){
							var chatdata=JSON.parse(res.data)
							let temp = {}
							temp = this.msgList.filter(v=>{
								return v.Id == chatdata.Id
							})
							if(temp.length == 0){
								this.msgList.push(chatdata)
							}
							
						} 
						var len = this.msgList.length
						if (len > 0) len = len - 1
						this.scrollToView = 'msg' + len
					});
				})
				// 这里仅是事件监听【如果socket关闭了会执行】
				this.socketTask.onClose(() => {
					console.log("已经被关闭了")
					this.is_open_socket = false;
					this.reconnect();
				})
			},
			//发送消息
			send(value) {
				var that = this;
				// 注：只有连接正常打开中 ，才能正常成功发送消息
				this.socketTask.send({
					data: value,
					success() {
						console.log("消息发送成功");
						var len = that.msgList.length
						if (len > 0) len = len - 1
						that.scrollToView = 'msg' + len
					},
				});
			},
			//开启心跳检测
			start() {
				this.heartbeatInterval = setInterval(() => {
					this.send('ping');
				}, this.timeout)
			},
			//重新连接
			reconnect() {
				//停止发送心跳
				clearInterval(this.heartbeatInterval)
				//如果不是人为关闭的话，进行重连
				if (this.isopensocket&&!this.is_open_socket) {
					this.reconnectTimeOut = setTimeout(() => {
						this.connectSocketInit();
					}, 3000)
				}
			},
			//关闭连接
			closeSocket() {
				//手动关闭连接
				this.isopensocket = false;
				this.socketTask.close({
					complete() {
						console.log('关闭连接...')
					}
				})
			},

			//处理图片尺寸，如果不处理宽高，新进入页面加载图片时候会闪
			setPicSize(row) {
				let maxW = uni.upx2px(350);
				let maxH = uni.upx2px(350);
				if (row.msg.w > maxW || row.msg.h > maxH) {
					let scale = row.msg.w / row.msg.h;
					if (row.msg.w > row.msg.h) {
						row.msg.w = maxW;
						row.msg.h = row.msg.w / scale
					} else {
						row.msg.h = maxH;
						row.msg.w = row.msg.h * scale;
					}
				}
				return row;
			},
			// 接受消息(筛选处理)
			screenMsg(msg) {
				//从长连接处转发给这个方法，进行筛选处理
				if (msg.uid != this.myuid) {
					uni.vibrateLong();
				}
				switch (msg.type) {
					case 'text':
						this.addTextMsg(msg);
						break;
					case 'voice':
						this.addVoiceMsg(msg);
						break;
					case 'img':
						this.addImgMsg(msg);
						break;
				}
				this.$nextTick(function() {
					this.scrollToView = 'msg' + msg.Id
				});
			},
			// 选择表情
			chooseEmoji() {
				this.showEmji = this.showEmji == 'showEmoji' ? 'hideEmoji' : 'showEmoji';
			},
			// 隐藏表情
			hideEmoji() {
				this.showEmji = this.showEmji == 'showEmoji' ? 'hideEmoji' : '';
			},
			//添加表情
			addEmoji(em) {
				this.textMsg += em.alt;
			},
			// 发送文字消息
			sendText() {
				this.hideEmoji();
				if (!this.textMsg) {
					return;
				}
				let content = this.replaceEmoji(this.textMsg);
				this.sendMsg(content, 0);
				this.textMsg = '';
			},
			//替换表情符号为图片
			replaceEmoji(str) {
				let replacedStr = str.replace(/\[([^(\]|\[)]*)\]/g, (item, index) => {
					console.log("item: " + item);
					for (let i = 0; i < this.emojiList.length; i++) {
						let row = this.emojiList[i];
						for (let j = 0; j < row.length; j++) {
							let EM = row[j];
							if (EM.alt == item) {
								//在线表情路径，图文混排必须使用网络路径
								let onlinePath = 'https://s2.ax1x.com/2019/04/12/'
								let imgstr = '<img src="' + onlinePath + this.onlineEmoji[EM.url] + '">';
								console.log("imgstr: " + imgstr);
								return imgstr;
							}
						}
					}
				});
				return '<div style="display: flex;align-items: center;">' + replacedStr + '</div>';
			},
			// 选择图片发送
			chooseImage() {
				var that = this;
				that.hideEmoji();
				uni.chooseImage({
					sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图，默认二者都有
					success: (res) => {
						for (let i = 0; i < res.tempFilePaths.length; i++) {
							that.$uploadFile.upload({
								url: '/api/FilesInfoApi/UploadFiles',
								filePath: res.tempFilePaths[i]
							}).then(rs => {
								if (rs.code == 200) {
									that.sendMsg(rs.data, 1);
								}
							})

							// uni.getImageInfo({
							// 	src: res.tempFilePaths[i],
							// 	success: (image) => {
							// 		console.log(image.width);
							// 		console.log(image.height);
							// 		let msg = {
							// 			url: res.tempFilePaths[i],
							// 			w: image.width,
							// 			h: image.height
							// 		};
							// 		this.sendMsg(msg, 1);
							// 	}
							// });
						}
					}
				});
			},
			// 发送消息
			sendMsg(content, type) {
				//实际应用中，此处应该提交长连接，模板仅做本地处理。
				var nowDate = new Date();
				var msg = {
					MsgType: type,
					UserId: this.myuid,
					ToUserId: this.touid,
					Contents: content,
					CrateTime: this.$u.timeFormat(nowDate, 'yyyy/mm/dd hh:MM:ss')
				}
				this.addTextMsg(msg)
				var value = `${this.touid}_${this.msgId}|${JSON.stringify(msg)}`
				this.send(value);
			},

			// 处理文字消息
			addTextMsg(msg) {
				this.msgList.push(msg);
			},
			// 处理语音消息
			addVoiceMsg(msg) {
				this.msgList.push(msg);
			},
			// 处理图片消息
			addImgMsg(msg) {
				// msg = this.setPicSize(msg);
				// this.msgImgList.push(msg.msg.url);
				this.msgList.push(msg);
			},
			// 预览图片
			showPic(row) {
				var url = this.getUrl(row.Contents)
				uni.previewImage({
					indicator: "none",
					current: url,
					urls: [url]
				});
			},
			// 播放语音
			playVoice(msg) {
				this.playMsgid = msg.id;
				this.AUDIO.src = msg.msg.url;
				console.log("msg.msg.url: " + msg.msg.url);
				this.AUDIO.play();
			},
			// 录音开始
			voiceBegin(e) {
				if (e.touches.length > 1) {
					return;
				}
				this.initPoint.Y = e.touches[0].clientY;
				this.initPoint.identifier = e.touches[0].identifier;
				this.RECORDER.start({
					format: "aac"
				}); //录音开始,
			},
			//录音开始UI效果
			recordBegin(e) {
				this.recording = true;
				this.voiceTis = '松开 结束';
				this.recordLength = 0;
				this.recordTimer = setInterval(() => {
					this.recordLength++;
				}, 1000)
			},
			// 录音被打断
			voiceCancel() {
				this.recording = false;
				this.voiceTis = '按住 说话';
				this.recordTis = '手指上滑 取消发送'
				this.willStop = true; //不发送录音
				this.RECORDER.stop(); //录音结束
			},
			// 录音中(判断是否触发上滑取消发送)
			voiceIng(e) {
				if (!this.recording) {
					return;
				}
				let touche = e.touches[0];
				//上滑一个导航栏的高度触发上滑取消发送
				if (this.initPoint.Y - touche.clientY >= uni.upx2px(100)) {
					this.willStop = true;
					this.recordTis = '松开手指 取消发送'
				} else {
					this.willStop = false;
					this.recordTis = '手指上滑 取消发送'
				}
			},
			// 结束录音
			voiceEnd(e) {
				if (!this.recording) {
					return;
				}
				this.recording = false;
				this.voiceTis = '按住 说话';
				this.recordTis = '手指上滑 取消发送'
				this.RECORDER.stop(); //录音结束
			},

			//录音结束(回调文件)
			recordEnd(e) {
				clearInterval(this.recordTimer);
				if (!this.willStop) {
					plus.io.resolveLocalFileSystemURL(e.tempFilePath, function(entry) {
						// 可通过entry对象操作test.html文件 
						entry.file(function(file) {
							console.log(file.size + '--' + file.name);
						});
					}, function(e) {
						alert("Resolve file URL failed: " + e.message);
					});
					console.log("e: " + JSON.stringify(e));
					let msg = {
						length: 0,
						url: e.tempFilePath
					}
					let min = parseInt(this.recordLength / 60);
					let sec = this.recordLength % 60;
					min = min < 10 ? '0' + min : min;
					sec = sec < 10 ? '0' + sec : sec;
					msg.length = min + ':' + sec;
					this.sendMsg(msg, 'voice');
				} else {
					console.log('取消发送录音');
				}
				this.willStop = false;
			},
			// 切换语音/文字输入
			switchVoice() {
				this.hideEmoji();
				this.isVoice = this.isVoice ? false : true;
			},
			discard() {
				return;
			}
		}
	}
</script>

<style lang="scss">
	page {
		background-color: #e5e5e5;
	}

	.imgmax {
		max-width: 160rpx;
		max-height: 160rpx;
	}

	.imgmax image {}

	.icon {
		font-size: 56upx;
		color: #333;
	}

	.hidden {
		display: none !important;
	}

	@keyframes showEM {
		0% {
			transform: translateY(0);
		}

		100% {
			transform: translateY(-42vw);
		}
	}

	@keyframes hideEM {
		0% {
			transform: translateY(-42vw);
		}

		100% {
			transform: translateY(0);
		}
	}

	.emoji-box {
		&.hideEmoji {
			animation: hideEM .15s linear both;
		}

		&.showEmoji {
			animation: showEM .15s linear both;
		}

		width: 96%;
		height: 42vw;
		padding: 20upx 2%;
		background-color: #f3f3f3;
		border-top: solid 1upx #ddd;
		position: fixed;
		z-index: 20;
		top: 100%;

		.swiper {
			swiper-item {
				display: flex;
				flex-wrap: wrap;

				view {
					width: 12vw;
					height: 12vw;
					display: flex;
					justify-content: center;

					image {
						width: 60%;
					}
				}
			}
		}
	}

	.input-box {
		&.hideEmoji {
			animation: hideEM .15s linear both;
		}

		&.showEmoji {
			animation: showEM .15s linear both;
		}

		width: 100%;
		min-height: 100upx;
		padding: 0 1% env(safe-area-inset-bottom);
		background-color: #f2f2f2;
		display: flex;
		position: fixed;
		z-index: 20;
		bottom: 0;

		.voice,
		.more {
			flex-shrink: 0;
			width: 90upx;
			height: 100upx;
			display: flex;
			justify-content: center;
			align-items: center;
		}

		.send {
			//H5发送按钮左边距
			/* #ifdef H5 */
			margin-left: 20upx;
			/* #endif */
			flex-shrink: 0;
			width: 100upx;
			height: 100upx;
			display: flex;
			align-items: center;

			.btn {
				width: 90upx;
				height: 56upx;
				display: flex;
				justify-content: center;
				align-items: center;
				background: #162a35;
				color: #fff04f;
				border-radius: 6upx;
				font-size: 24upx;
			}
		}

		.textbox {
			width: 100%;
			min-height: 70upx;
			margin-top: 15upx;

			.voice-mode {
				width: calc(100% - 2upx);
				height: 68upx;
				border-radius: 70upx;
				border: solid 1upx #cdcdcd;
				display: flex;
				justify-content: center;
				align-items: center;
				font-size: 28upx;
				background-color: #fff;
				color: #555;

				&.recording {
					background-color: #e5e5e5;
				}
			}

			.text-mode {
				width: 100%;
				min-height: 70upx;
				display: flex;
				background-color: #fff;
				border-radius: 40upx;

				.box {
					width: 100%;
					padding-left: 30upx;
					min-height: 70upx;
					display: flex;
					align-items: center;

					textarea {
						width: 100%;
						max-height: 300rpx;
					}
				}

				.em {
					flex-shrink: 0;
					width: 80upx;
					height: 70upx;
					display: flex;
					justify-content: center;
					align-items: center;
				}
			}
		}
	}

	.record {
		width: 40vw;
		height: 40vw;
		position: fixed;
		top: 55%;
		left: 30%;
		background-color: rgba(0, 0, 0, .6);
		border-radius: 20upx;

		.ing {
			width: 100%;
			height: 30vw;
			display: flex;
			justify-content: center;
			align-items: center;

			@keyframes volatility {
				0% {
					background-position: 0% 130%;
				}

				20% {
					background-position: 0% 150%;
				}

				30% {
					background-position: 0% 155%;
				}

				40% {
					background-position: 0% 150%;
				}

				50% {
					background-position: 0% 145%;
				}

				70% {
					background-position: 0% 150%;
				}

				80% {
					background-position: 0% 155%;
				}

				90% {
					background-position: 0% 140%;
				}

				100% {
					background-position: 0% 135%;
				}
			}

			.icon {
				background-image: linear-gradient(to bottom, #f09b37, #fff 50%);
				background-size: 100% 200%;
				animation: volatility 1.5s ease-in-out -1.5s infinite alternate;
				-webkit-background-clip: text;
				-webkit-text-fill-color: transparent;
				font-size: 150upx;
				color: #f09b37;
			}
		}

		.cancel {
			width: 100%;
			height: 30vw;
			display: flex;
			justify-content: center;
			align-items: center;

			.icon {
				color: #fff;
				font-size: 150upx;
			}
		}

		.tis {
			width: 100%;
			height: 10vw;
			display: flex;
			justify-content: center;
			font-size: 28upx;
			color: #fff;

			&.change {
				color: #f09b37;
			}
		}
	}

	.content {
		width: 100%;

		.msg-list {
			width: 96%;
			padding: 0 2%;
			position: absolute;
			top: 0;
			bottom: 100upx;

			.row {
				&:first-child {
					margin-top: 20upx;
				}

				padding: 20upx 0;

				.my .left,
				.other .right {
					width: 100%;
					display: flex;

					.bubble {
						max-width: 70%;
						min-height: 50upx;
						border-radius: 10upx;
						padding: 15upx 20upx;
						display: flex;
						align-items: center;
						font-size: 32upx;
						word-break: break-word;

						&.img {
							background-color: transparent;
							padding: 0;
							overflow: hidden;

							image {
								max-width: 350upx;
								max-height: 350upx;
							}
						}

						&.voice {
							.icon {
								font-size: 40upx;
								display: flex;
								align-items: center;
							}

							.icon:after {
								content: " ";
								width: 53upx;
								height: 53upx;
								border-radius: 100%;
								position: absolute;
								box-sizing: border-box;
							}

							.length {
								font-size: 28upx;
							}
						}
					}
				}

				.my .right,
				.other .left {
					flex-shrink: 0;
					width: 80upx;
					height: 80upx;

					image {
						width: 80upx;
						height: 80upx;
						border-radius: 50%;
					}
				}

				.my {
					width: 100%;
					display: flex;
					justify-content: flex-end;

					.left {
						min-height: 80upx;

						align-items: center;
						justify-content: flex-end;

						.bubble {
							background-color: #fff04f;
							color: #162a35;

							&.voice {
								.icon {
									color: #fff;
								}

								.length {
									margin-right: 20upx;
								}
							}

							&.play {
								@keyframes my-play {
									0% {
										transform: translateX(80%);
									}

									100% {
										transform: translateX(0%);
									}
								}

								.icon:after {
									border-left: solid 10upx rgba(240, 108, 122, .5);
									animation: my-play 1s linear infinite;
								}
							}
						}
					}

					.right {
						margin-left: 15upx;
					}
				}

				.other {
					width: 100%;
					display: flex;

					.left {
						margin-right: 15upx;
					}

					.right {
						flex-wrap: wrap;

						.username {
							width: 100%;
							height: 45upx;
							font-size: 24upx;
							color: #999;
							display: flex;

							.name {
								margin-right: 50upx;
							}
						}

						.bubble {
							background-color: #fff;
							color: #333;

							&.voice {
								.icon {
									color: #333;

								}

								.length {
									margin-left: 20upx;
								}
							}

							&.play {
								@keyframes other-play {
									0% {
										transform: translateX(-80%);
									}

									100% {
										transform: translateX(0%);
									}
								}

								.icon:after {
									border-right: solid 10upx rgba(255, 255, 255, .8);

									animation: other-play 1s linear infinite;
								}
							}
						}
					}
				}
			}
		}
	}
</style>
